package com.framework.common.exception;

import com.framework.common.AjaxResult;
import com.framework.common.ResultCode;
import com.framework.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * @version V1.0
 * @program: teamwork
 * @package: com.framework.common.exception
 * @description: 统一异常处理类
 * @author: lzd
 * @create: 2020-06-27 12:58
 **/
@Slf4j
@Component
@ControllerAdvice
public class GlobalExceptionHandler {

    private void _checkException(Throwable e) {
        String eInfo = "";
        if(e != null && e.getMessage() != null) {
            eInfo = e.getMessage();
        }

        String msg = "";
        if(eInfo.contains("OutOfMemoryError")) {
            msg = "OutOfMemoryError";
        }
        if(eInfo.contains("Communications link failure")) {
            msg = "Communications link failure";
        }
        if(eInfo.contains("Could not open JDBC Connection for transaction")) {
            msg = "DB connection fail:Could not open JDBC Connection for transaction";
        }
        if(eInfo.contains("java.sql.SQLException: Connection is closed")) {
            msg = "DB connection is closed";
        }
        if(eInfo.contains("sync db connect fail")) {
            msg = "sync db connect fail";
        }
        if(eInfo.contains("Failed to obtain JDBC Connection")) {
            msg = "Failed to obtain JDBC Connection";
        }
        if (eInfo.contains("The last packet successfully received from the server")) {
            msg = "Auto reconnect fail";
        }
        if (eInfo.contains("Unknown database '")) {
            msg = "Not found database";
        }
        if(eInfo.contains("Lock wait timeout exceeded")) {
            msg = "Lock wait timeout exceeded";
        }
        if(eInfo.contains("Deadlock found when trying to get lock")) {
            msg = "Deadlock found when trying to get lock";
        }
        if(eInfo.contains("restart for config")) {
            msg = "restart for config";
        }
        if(!StringUtils.isEmpty(msg)) {
             log.error("严重错误(需自动重启):" + eInfo);
             System.exit(1);
        }
    }

    private void checkException(Throwable e) {
        _checkException(e);
        if(e.getCause() != null) {
            _checkException(e.getCause());
        }
    }
    /**
     * 自定义异常
     *
     * @param e 异常
     * @return
     */
    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public AjaxResult handleBaseException(CustomException e) {
        log.debug("异常信息：{}", e.getMessage());
        checkException(e);
        return AjaxResult.warn(e.getMessage());
    }

    /**
     * Controller上一层相关异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(value = {
            NoHandlerFoundException.class,
            HttpRequestMethodNotSupportedException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingPathVariableException.class,
            MissingServletRequestParameterException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMessageNotWritableException.class,
    })
    @ResponseBody
    public AjaxResult handleServletException(Exception e) {
        log.error(e.getMessage(), e);
        checkException(e);
        if (e instanceof NoHandlerFoundException) {
            return new AjaxResult(ResultCode.NOT_FOUND, e.getMessage());
        } else if (e instanceof HttpRequestMethodNotSupportedException) {
            return new AjaxResult(ResultCode.METHOD_NOT_ALLOWED, e.getMessage());
        } else if (e instanceof HttpMediaTypeNotSupportedException) {
            return new AjaxResult(ResultCode.JSON_FORMAT_ERROR, e.getMessage());
        } else if (e instanceof MissingPathVariableException) {
            return new AjaxResult(ResultCode.NOT_FOUND, e.getMessage());
        } else if (e instanceof MissingServletRequestParameterException) {
            return new AjaxResult(ResultCode.BAD_REQUEST, e.getMessage());
        } else if (e instanceof TypeMismatchException) {
            return new AjaxResult(ResultCode.BAD_REQUEST, e.getMessage());
        } else if (e instanceof HttpMessageNotReadableException) {
            return new AjaxResult(ResultCode.JSON_FORMAT_ERROR, e.getMessage());
        } else {
            return new AjaxResult(ResultCode.JSON_FORMAT_ERROR, e.getMessage());
        }
    }

    /**
     * 参数绑定异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public AjaxResult handleBindException(BindException e) {
        log.error("参数绑定校验异常", e);
        checkException(e);
        return wrapperBindingResult(e.getBindingResult());
    }

    /**
     * 参数校验异常，将校验失败的所有异常组合成一条错误信息
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public AjaxResult handleValidException(MethodArgumentNotValidException e) {
        log.error("参数绑定校验异常", e);
        checkException(e);
        return wrapperBindingResult(e.getBindingResult());
    }

    /**
     * 包装绑定异常结果
     *
     * @param bindingResult 绑定结果
     * @return 异常结果
     */
    private AjaxResult wrapperBindingResult(BindingResult bindingResult) {
        StringBuilder msg = new StringBuilder();
        for (ObjectError error : bindingResult.getAllErrors()) {
            if (error instanceof FieldError) {
                msg.append(((FieldError) error).getField()).append(": ");
            }
            msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
            msg.append(", ");
        }
        return new AjaxResult(ResultCode.BAD_REQUEST, msg.toString());
    }

    /**
     * 用户名密码错误
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(value = BadCredentialsException.class)
    @ResponseBody
    public AjaxResult handleException(BadCredentialsException e) {
        e.printStackTrace();
        log.debug(e.getMessage(), e);
        checkException(e);
        return new AjaxResult(ResultCode.USER_ERROR_EXCEPTION, e.getMessage());
    }

    /**
     * 未定义异常
     *
     * @param e 异常
     * @return 异常结果
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public AjaxResult handleException(Exception e) {
        e.printStackTrace();
        log.debug(e.getMessage(), e);
        checkException(e);
        return new AjaxResult(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }

    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public AjaxResult handleRuntimeException(RuntimeException e) {
        e.printStackTrace();
        log.debug(e.getMessage(), e);
        checkException(e);
        return new AjaxResult(ResultCode.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}
